package top.lingkang.ciyuanwaf.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.boot.web.OutputUtils;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.MethodType;
import org.noear.solon.core.handle.ModelAndView;
import org.noear.solon.core.handle.UploadedFile;
import top.lingkang.ciyuanwaf.WebApp;
import top.lingkang.ciyuanwaf.constants.LogType;
import top.lingkang.ciyuanwaf.dao.FileDao;
import top.lingkang.ciyuanwaf.dao.UnzipFileDao;
import top.lingkang.ciyuanwaf.dto.ResponseResult;
import top.lingkang.ciyuanwaf.entity.FileEntity;
import top.lingkang.ciyuanwaf.entity.UnzipFileEntity;
import top.lingkang.ciyuanwaf.service.LogService;
import top.lingkang.ciyuanwaf.utils.CheckUtils;
import top.lingkang.ciyuanwaf.utils.CommonUtils;
import top.lingkang.ciyuanwaf.utils.IdUtils;
import top.lingkang.ciyuanwaf.vo.FileListVO;
import top.lingkang.hibernate6.transaction.Transaction;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lingkang
 * Created by 2023/11/26
 */
@Slf4j
@Controller
@Mapping("/file")
public class FileController {
    @Inject
    private FileDao fileDao;
    @Inject
    private UnzipFileDao unzipFileDao;
    @Inject
    private LogService logService;

    @Mapping(value = "/index", method = MethodType.GET)
    public Object index() {
        ModelAndView view = new ModelAndView("uploadFileManage.ftl");

        List<FileEntity> all = fileDao.findAll();
        view.put("list", all);
        return view;
    }

    @Transaction
    @Mapping(value = "/upload", method = MethodType.POST)
    public Object upload(Context context) throws Exception {
        UploadedFile file = context.file("file");
        FileEntity entity = new FileEntity();
        entity.setId(IdUtils.id());
        entity.setSuffix(file.getExtension());
        entity.setSize(file.getContentSize());
        entity.setOriginalName(file.getName());

        File storeFile = new File(WebApp.dir + "data"
                + File.separator + "file"
                + File.separator + entity.getId() + "." + entity.getSuffix());
        if (!storeFile.getParentFile().exists())
            storeFile.getParentFile().mkdirs();

        file.transferTo(storeFile);
        entity.setPath(storeFile.getAbsolutePath());
        fileDao.saveOrUpdate(entity);
        logService.file(LogType.upload_file, storeFile.getAbsolutePath(), storeFile.length());
        return new ResponseResult<>("上传成功");
    }

    @Mapping(value = "/download", method = MethodType.GET)
    public void download(String id, Context context) throws IOException {
        CheckUtils.checkNotEmpty("id", "id");
        FileEntity file = fileDao.findById(id);
        CheckUtils.checkNotNull(file, "文件不存在/或被删除");
        context.outputAsFile(new File(file.getPath()));
        logService.file(LogType.download_file, file.getPath(), file.getSize());
    }

    @Transaction
    @Mapping(value = "/delete", method = MethodType.POST)
    public Object delete(String id, Context context) {
        CheckUtils.checkNotEmpty(id, "id");
        FileEntity file = fileDao.findById(id);
        CheckUtils.checkNotNull(file, "文件不存在/或被删除");
        File deleteFile = new File(file.getPath());
        if (deleteFile.exists())
            deleteFile.delete();
        fileDao.delete(file);
        return new ResponseResult<>("删除成功！");
    }

    @Transaction
    @Mapping(value = "/unzip", method = MethodType.POST)
    public Object unzip(String id, String path, Context context) {
        CheckUtils.checkNotEmpty("id", "id");
        CheckUtils.checkNotEmpty(path, "解压到的路径");
        FileEntity file = fileDao.findById(id);
        CheckUtils.checkNotNull(file, "文件不存在/或被删除");
        if (!"zip".equals(file.getSuffix()))
            return new ResponseResult<>().fail("文件不是压缩文件");
        File to = new File(path);
        if (!to.exists()) {
            boolean mkdirs = to.mkdirs();
            if (!mkdirs)
                return new ResponseResult<>().fail("创建目录失败：" + path);
        }

        File zip = new File(file.getPath());
        ZipUtil.unzip(zip, to, StandardCharsets.UTF_8);
        UnzipFileEntity entity = new UnzipFileEntity();
        entity.setId(IdUtils.id());
        entity.setStatus("解压");
        entity.setParentId(file.id);
        entity.setPath(to.getAbsolutePath());
        unzipFileDao.saveOrUpdate(entity);
        logService.file(LogType.unzip_file, file.getPath(), file.getSize());
        return new ResponseResult<>("解压完成！");
    }


    private String lastPath = WebApp.dir;

    @Mapping(value = "/pathSelect", method = MethodType.GET)
    public Object pathSelect(String isFolder) {
        return new ModelAndView("path.ftl")
                .put("current", lastPath)
                .put("isFolder", isFolder);
    }

    @Mapping(value = "/pathSelect", method = MethodType.POST)
    public Object pathSelect(String isFolder, String path, String back) {
        if (StrUtil.isBlank(path))
            path = WebApp.dir;
        boolean toFolder = true;
        if (StrUtil.isBlank(isFolder) || "false".equals(isFolder))
            toFolder = false;

        if (path.contains("&amp;"))
            path = path.replaceAll("&amp;", "&");
        File file = new File(path);
        if (!file.exists()) {
            return new ResponseResult<>().fail("不存在的路径：" + file.getAbsolutePath());
        }
        JSONObject result = new JSONObject();
        if ("1".equals(back) && file.isDirectory()) {
            if (file.getParentFile() == null)
                return new ResponseResult<>().fail("已经是最上级目录了");
            result.put("list", CommonUtils.getFileListName(file.getParentFile(), toFolder));
            result.put("current", file.getParentFile().getAbsolutePath());
        } else {
            result.put("list", CommonUtils.getFileListName(file, toFolder));
            result.put("current", path);
        }

        lastPath = path;
        return new ResponseResult<>().setData(result);
    }


    private String fileList = WebApp.dir;

    @Mapping(value = "/manage", method = MethodType.GET)
    public Object manage() {
        ModelAndView view = new ModelAndView("fileManage.ftl").put("path", fileList.replaceAll("\\\\", "//"));
        return view;
    }

    @Mapping(value = "/list", method = MethodType.GET)
    public Object list(String path, String isBack, String isProject) {
        if ("true".equals(isProject) && StrUtil.isBlank(path))
            path = WebApp.dir;
        CheckUtils.checkNotEmpty(path, "路径");
        File file = new File(path);
        if (!file.exists()) {
            return new ResponseResult<>().fail("路径不存在: " + path);
        }
        if (!file.isDirectory())
            file = file.getParentFile();
        if ("true".equals(isBack)) {
            File parentFile = file.getParentFile();
            if (parentFile == null)
                return new ResponseResult<>().fail("已经是最上级目录了");
            file = parentFile;
        }

        JSONObject data = new JSONObject();
        data.put("path", file.getAbsolutePath());
        List<FileListVO> list = new ArrayList<>();
        for (File item : file.listFiles()) {
            FileListVO vo = new FileListVO();
            vo.setName(item.getName());
            vo.setPath(item.getAbsolutePath());
            vo.setSize(item.length());
            vo.setLastUpdateTime(item.lastModified());
            vo.setFolder(item.isDirectory());
            list.add(vo);
        }
        list.sort((o1, o2) -> {
            return CharSequence.compare(o1.getName(), o2.getName());
        });
        list.sort((o1, o2) -> {
            return Boolean.compare(o2.isFolder(), o1.isFolder());
        });
        data.put("list", list);
        fileList = file.getAbsolutePath();
        return new ResponseResult<>().setData(data);
    }

    @Mapping(value = "/open", method = MethodType.GET)
    public Object open(String path, Context context) throws Exception {
        CheckUtils.checkNotEmpty(path, "路径");
        File file = new File(path);
        if (!file.exists())
            return new ResponseResult<>().fail("文件不存在: " + file.getAbsolutePath());
        if (file.isDirectory())
            return new ResponseResult<>().fail("路径是一个目录，打开失败：" + file.getAbsolutePath());

        String contentType = Utils.mime(file.getName());
        if (Utils.isNotEmpty(contentType)) {
            context.contentType(contentType);
        }

        OutputUtils.global().outputFile(context, file, false);
        logService.file(LogType.open_file, file.getPath(), file.length());
        return null;
    }

    @Mapping(value = "/downloadZip", method = MethodType.GET)
    public Object downloadZip(String path, Context context) throws Exception {
        CheckUtils.checkNotEmpty(path, "路径");
        File file = new File(path);
        if (!file.exists())
            return new ResponseResult<>().fail("文件不存在: " + file.getAbsolutePath());
        File zip = null;

        try {
            zip = ZipUtil.zip(file);
            context.outputAsFile(zip);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            zip.delete();
        }
        logService.file(LogType.pack_download_file, file.getPath(), file.length());
        return null;
    }

    @Mapping(value = "/downloadZipList", method = MethodType.GET)
    public Object downloadZipList(String list, Context context) throws Exception {
        CheckUtils.checkNotEmpty(list, "列表");
        JSONArray parse = JSONArray.parse(list);
        if (parse.isEmpty())
            return new ResponseResult<>().fail("文件列表不能为空");
        List<File> files = new ArrayList<>();
        for (Object path : parse) {
            File file = new File(path.toString());
            if (!file.exists())
                return new ResponseResult<>().fail("文件不存在: " + file.getAbsolutePath());
            files.add(file);
        }

        File zip = null;
        try {
            zip = ZipUtil.zip(FileUtil.file(
                            WebApp.dir + "data" + File.separator + System.currentTimeMillis() + ".zip"),
                    false,
                    files.toArray(new File[0])
            );
            context.outputAsFile(zip);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            zip.delete();
        }
        logService.file(LogType.pack_download_list_file, parse.toString(), 0);
        return null;
    }

    @Mapping(value = "/deletePath", method = MethodType.POST)
    public Object deletePath(String path, Context context) {
        CheckUtils.checkNotEmpty(path, "路径");
        File file = new File(path);
        if (!file.exists())
            return new ResponseResult<>().fail("文件不存在/或被删除: " + path);
        log.warn("删除文件/目录：{}", path);
        file.delete();
        logService.file(LogType.delete_file, file.getAbsolutePath(), file.length());
        return new ResponseResult<>("删除成功！");
    }

    @Mapping(value = "/uploadPath", method = MethodType.POST)
    public Object uploadPath(Context context, String path) throws IOException {
        CheckUtils.checkNotEmpty(path, "保存路径");
        File file = new File(path);
        if (!file.exists())
            return new ResponseResult<>().fail("目录不存在/或被删除: " + path);
        if (!file.isDirectory())
            return new ResponseResult<>().fail("路径不是目录: " + path);

        UploadedFile uploadedFile = context.file("file");
        file = new File(file.getAbsolutePath() + File.separator + uploadedFile.getName());
        if (file.exists()) {
            return new ResponseResult<>().fail("上传失败，已经存在文件：" + file.getAbsolutePath());
        }
        uploadedFile.transferTo(file);
        logService.file(LogType.upload_file, file.getAbsolutePath(), file.length());
        return new ResponseResult<>("上传成功");
    }

    @Mapping(value = "/move", method = MethodType.POST)
    public Object move(String list, String path, Context context) throws Exception {
        CheckUtils.checkNotEmpty(list, "要移动的文件列表");
        CheckUtils.checkNotEmpty(path, "移动到的路径");
        JSONArray parse = JSONArray.parse(list);
        if (parse.isEmpty())
            return new ResponseResult<>().fail("要移动的文件列表不能为空");
        List<File> files = new ArrayList<>();
        for (Object filePath : parse) {
            File file = new File(filePath.toString());
            if (!file.exists())
                return new ResponseResult<>().fail("文件/文件夹不存在: " + file.getAbsolutePath());
            files.add(file);
        }

        File file = new File(path);
        if (!file.exists())
            return new ResponseResult<>().fail("不存在的目标路径：" + path);
        if (!file.isDirectory())
            return new ResponseResult<>().fail("目标路径不是文件夹：" + path);

        for (File f : files) {
            FileUtil.move(f, file, true);
            logService.file(LogType.move_file, f.getAbsolutePath() + "->" + file.getAbsolutePath(), file.length());
        }
        return new ResponseResult<>("移动完成");
    }

    @Mapping(value = "/copy", method = MethodType.POST)
    public Object copy(String list, String path, Context context) throws Exception {
        CheckUtils.checkNotEmpty(list, "要复制的文件列表");
        CheckUtils.checkNotEmpty(path, "复制到的路径");
        JSONArray parse = JSONArray.parse(list);
        if (parse.isEmpty())
            return new ResponseResult<>().fail("要复制的文件列表不能为空");
        List<File> files = new ArrayList<>();
        for (Object filePath : parse) {
            File file = new File(filePath.toString());
            if (!file.exists())
                return new ResponseResult<>().fail("文件/文件夹不存在: " + file.getAbsolutePath());
            files.add(file);
        }

        File file = new File(path);
        if (!file.exists())
            return new ResponseResult<>().fail("不存在的目标路径：" + path);
        if (!file.isDirectory())
            return new ResponseResult<>().fail("目标路径不是文件夹：" + path);

        for (File f : files) {
            FileUtil.copy(f, new File(file.getAbsoluteFile() + File.separator + f.getName()), true);
            logService.file(LogType.copy_file, f.getAbsolutePath() + "->" + file.getAbsolutePath(), file.length());
        }

        return new ResponseResult<>("复制完成");
    }

    @Mapping(value = "/rename", method = MethodType.POST)
    public Object rename(String name, String path, Context context) throws Exception {
        CheckUtils.checkNotEmpty(name, "新名称");
        CheckUtils.checkNotEmpty(path, "原文件路径");

        File file = new File(path);
        if (!file.exists())
            return new ResponseResult<>().fail("原文件不存在：" + path);
        File rename = FileUtil.rename(file, name, false);
        logService.file(LogType.rename_file, file.getAbsolutePath() + "->" + rename.getAbsolutePath(), 0);
        return new ResponseResult<>("重命名完成");
    }
}
